<?php
/**
 * panx后台设备的管理
 * Author: lyun
 * Date: 2018/3/26
 * Created by Panxsoft.
 */


namespace App\Http\Controllers\Admin\Panx;

use App\Http\Controllers\ApiController;
use App\Http\Requests\Admin\Panx\Device\DeviceBuyRequest;
use App\Http\Requests\Admin\Panx\Device\DeviceIndexRequest;
use App\Http\Requests\Admin\Panx\Device\DeviceSellListRequest;
use App\Http\Requests\Admin\Panx\Device\DeviceSellRequest;
use App\Http\Requests\Admin\Panx\Device\DeviceStoreRequest;
use App\Http\Requests\Admin\Panx\Device\DeviceUpdateDeviceAccountRequest;
use App\Http\Requests\Admin\Panx\Device\DeviceUpdateRequest;
use App\Models\Admin\PanxAdmin;
use App\Models\Customer\Customer;
use App\Models\Customer\DeviceAccount;
use App\Models\Panx\Device;
use App\Models\Panx\DeviceAccountPivotSkinDetector;
use App\Models\Panx\DeviceSpec;
use App\Models\Panx\PurchaseDevice;
use App\Models\Panx\SellDevice;
use App\Models\Panx\SkinDetector;
use App\Services\Utils\Transaction;
use App\Services\Utils\Uuid;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Tests\Feature\App\Admin\Panx\DeviceControllerTest;

class DeviceController extends ApiController
{
    const DEFAULT_ORDER_BYS_INDEX = [
        ['order_by' => 'purchase_at', 'sort' => 'DESC'],
    ];

    const DEFAULT_ORDER_BYS_SELL_LIST = [
        ['order_by' => 'sold_at', 'sort' => 'DESC'],
    ];

    const ROUTES = [
        'index'                 => 'admin.panx.device.index',
        'device_list'           => 'admin.panx.device.device.list',
        'sell_list'             => 'admin.panx.device.sell.list',
        'store'                 => 'admin.panx.device.store',
        'update'                => 'admin.panx.device.update',
        'buy_device'            => 'admin.panx.device.buy.device',
        'sell_device'           => 'admin.panx.sell_device',
        'update_device_account' => 'admin.panx.update.device.account'
    ];

    /**
     * 设备采购的列表
     *
     * @test
     * @see DeviceControllerTest::test_index()
     *
     * @param DeviceIndexRequest $request
     * @return \Illuminate\Http\Response
     * @throws \Exception
     */
    public function index(DeviceIndexRequest $request)
    {
        // 超管才能操作的
        $this->checkIsTrue(PanxAdmin::checkIsSuperMan(), trans('admin.is_super_man'));

        $purchase_devices = (new PurchaseDevice)
            ->filterDeviceCode($request->device_code)
            ->filterDeviceName($request->device_name)
            ->filterSpecification($request->specification)
            ->filterProducer($request->producer)
            ->filterBuyer($request->buyer)
            ->filterTimeRange($request->time_range, 'purchase_at')
            ->orderBys($request->order_bys, self::DEFAULT_ORDER_BYS_INDEX)
            ->with('device')
            ->paginate($request->per_page ?: 15);

        return $this->response(compact('purchase_devices'));
    }

    /**
     * 返回devices 表中的数据
     *
     * @test
     *
     * @param Request $request
     * @return array|\Illuminate\Http\Response
     * @throws \Exception
     */
    public function deviceList(Request $request)
    {
        $this->checkIsTrue(PanxAdmin::checkIsSuperMan(), trans('admin.is_super_man'));

        $devices = (new Device)->filterDeviceName($request->device_name)
            ->filterCode($request->code)
            ->filterSpecification($request->specification)
            ->filterProducer($request->producer)
            ->latest()
            ->with(['deviceSpec'])
            ->paginate($request->per_page ?: 15);

        return $this->response(compact('devices'));
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        // 返回skin_detectors表的数据
        $skin_detectors = SkinDetector::filterDeleted(SkinDetector::NOT_DELETED)
            ->pluck('type', 'id')->all();

        // 返回devices表的数据
        $devices = Device::with('skinDetector')->get();

        // 返回customers表的数据
        $customers = Customer::where('is_deleted', Customer::IS_NOT_DELETED)
            ->where('provider_type', '<>', Customer::PROVIDER_TYPE_VIRTUAL)
            ->pluck('name', 'id');

        // 返回device_specs表的数据
        $device_specs = DeviceSpec::get(['id','code']);

        return $this->response(compact('skin_detectors', 'devices', 'customers','device_specs'));
    }

    /**
     * 添加新的设备型号和采购信息
     *
     * @test
     *
     * @param  DeviceStoreRequest $request
     * @return \Illuminate\Http\Response
     * @throws \Exception
     */
    public function store(DeviceStoreRequest $request)
    {
        // 超管才能操作的
        $this->checkIsTrue(PanxAdmin::checkIsSuperMan(), trans('admin.is_super_man'));

        $device = $this->assembleDeviceData($request);

        $stored = Transaction::handle(function() use($request, $device) {

            $device = Device::create($device);
            $purchase_device = $this->assemblePurchaseDevice($request, $device);
            PurchaseDevice::create($purchase_device);

            return $device;
        });

        return $this->response(compact('stored'));
    }

    /**
     * 仅仅更新设备(devices)试妆参数(device_spec_id)
     *
     * @test
     * @see DeviceControllerTest::test_update()
     *
     * @param DeviceUpdateRequest $request
     * @param $id
     * @return array|\Illuminate\Http\Response
     * @throws \Exception
     */
    public function update(DeviceUpdateRequest $request, $id)
    {
        $device = (new Device)->find($id);
            $this->checkItemExist($device);

        $update = $device->update([
            'device_spec_id' => $request->device_spec_id,
        ]);

        return $this->response(compact('update'));

    }

    /**
     *  选择已有的设备型号，添加采购数据
     *
     * @test
     * @see DeviceControllerTest::test_buy_device()
     *
     * @param DeviceBuyRequest $request
     * @return array|\Illuminate\Http\Response
     * @throws \Exception
     */
    public function buyDevice(DeviceBuyRequest $request)
    {
        // 超管才能操作的
        $this->checkIsTrue(PanxAdmin::checkIsSuperMan(), trans('admin.is_super_man'));

        /** @var Device $device */
        $device = Device::find($request->device_id);
            $this->checkItemExist($device, trans('admin.device'));

        $purchase_device = $this->assemblePurchaseDevice($request, $device);

        $stored = PurchaseDevice::create($purchase_device);

        return $this->response(compact('stored'));
    }

    /**
     * 保存卖出设备的记录
     *
     * @test
     *
     * @param DeviceSellRequest $request
     * @return array|\Illuminate\Http\Response
     * @throws \Exception
     */
    public function sellDevice(DeviceSellRequest $request)
    {
        // 超管才能操作的
        $this->checkIsTrue(PanxAdmin::checkIsSuperMan(), trans('admin.is_super_man'));

        /** @var PurchaseDevice $purchase_device */
        $purchase_device = PurchaseDevice::find($request->purchase_device_id);
            $this->checkItemExist($purchase_device, trans('admin.purchase_device'));

        $stock = $this->validateStock($purchase_device, $request->num);

        $sell_device = $this->assembleSellDevice($request);
        $uuids = $this->normalizeUUIDs($request);
        $skin_detector_ids = $request->skin_detector_ids;

        $stored = Transaction::handle(function() use($stock, $sell_device, $purchase_device, $uuids, $skin_detector_ids) {
            // 保存设备卖出记录
            $sell_device = SellDevice::create($sell_device);
            // 更新设备库存数据
            $purchase_device->update(['stock'=>$stock]);
            // 生成设备账号
            $this->storeDeviceAccount($purchase_device,$sell_device, $uuids);
            // 设备账号和肤质检测仪类型的关联
            $this->storeDeviceAccountPivotSkinDetector($sell_device, $skin_detector_ids);

            return $sell_device;
        });

        return $this->response(compact('stored'));
    }

    /**
     * 返回已经卖出的设备信息
     *
     * @test
     * @see DeviceControllerTest::test_sell_list()
     *
     * @param DeviceSellListRequest $request
     * @return array|\Illuminate\Http\Response
     * @throws \Exception
     */
    public function sellList(DeviceSellListRequest $request)
    {
        // 超管才能操作的
        $this->checkIsTrue(PanxAdmin::checkIsSuperMan(), trans('admin.is_super_man'));

        // 查询字段
        $query_data = [
            'code'          => $request->device_code,
            'device_name'   => $request->device_name,
            'specification' => $request->specification,
            'producer'      => $request->producer,
        ];

        /** @var LengthAwarePaginator $sell_devices */
        $sell_devices = (new SellDevice)
            ->filterDevice($query_data)
            ->filterOperator($request->operator)
            ->filterCustomerId($request->customer_id)
            ->filterTimeRange($request->time_range, 'sold_at')
            ->orderBys($request->order_bys, self::DEFAULT_ORDER_BYS_SELL_LIST)
            ->with('purchaseDevice.device', 'deviceAccounts.store', 'deviceAccounts.skinDetectors')
            ->paginate($request->per_page?:15);

        $this->transformData($sell_devices);

        return $this->response(compact('sell_devices'));
    }

    /**
     * 超管权限下，修改设备账号的信息。
     * @test
     *
     * @param DeviceUpdateDeviceAccountRequest $request
     * @return array|\Illuminate\Http\Response
     * @throws \Exception
     */
    public function updateDeviceAccount(DeviceUpdateDeviceAccountRequest $request)
    {
        // 超管才能操作的
        $this->checkIsTrue(PanxAdmin::checkIsSuperMan(), trans('admin.is_super_man'));

        $device_account = (new DeviceAccount)->find($request->id);
            $this->checkItemExist($device_account, '设备账号');

        $device_account_data = $this->assembleDeviceAccountData($request);
        $skin_detector_ids = $request->skin_detector_ids ?: [];

        $updated = Transaction::handle(function() use($device_account, $device_account_data, $skin_detector_ids){
            $updated = $device_account->update($device_account_data);

            $device_account->skinDetectors()->sync($skin_detector_ids);

            return $updated;
        });

        return $this->response(compact('updated'));
    }

    /**
     * @param DeviceStoreRequest $request
     * @return array
     */
    private function assembleDeviceData(DeviceStoreRequest $request)
    {
        return [
            'uuid'             => Uuid::generate(),
            'code'             => $request->device_code,
            'device_name'      => $request->device_name,
            'specification'    => $request->specification,
            'producer'         => $request->producer,
            'device_spec_id'   => $request->device_spec_id,
        ];
    }

    /**
     * @param DeviceStoreRequest|DeviceBuyRequest|Request $request
     * @param Device $device
     * @return array
     */
    private function assemblePurchaseDevice(Request $request, Device $device)
    {
        return [
            'device_id'        => $device->id,
            'sku'              => $request->sku,
            'stock'            => $request->sku,
            'buyer'            => $request->buyer,
            'purchase_at'      => $request->purchase_at,
        ];
    }

    /**
     * @param DeviceSellRequest $request
     * @return array
     */
    private function assembleSellDevice(DeviceSellRequest $request)
    {
        return [
            'purchase_device_id' => $request->purchase_device_id,
            'customer_id'        => $request->customer_id,
            'num'                => $request->num,
            'sold_at'            => $request->sold_at,
            'operator'           => $request->operator,
        ];
    }

    /**
     * @param PurchaseDevice $purchase_device
     * @param $num
     * @return mixed
     * @throws \Exception
     */
    private function validateStock(PurchaseDevice $purchase_device, $num)
    {
        $stock = $purchase_device->stock-$num;

        $this->checkIsTrue($stock>=0, trans('admin.stock'));

        return $stock;
    }

    /**
     * @param PurchaseDevice $purchase_device
     * @param SellDevice $sell_device
     * @param string[] $uuids
     */
    private function storeDeviceAccount(
        PurchaseDevice $purchase_device,
        SellDevice $sell_device,
        array $uuids
    )
    {
        $device = $purchase_device->device;
        $sell_num   = $sell_device->num;
        $customer_id = $sell_device->customer_id;

        $new_accounts = [];

        for($index = 0; $index < $sell_num; $index++)
        {
            $new_accounts []= [
                'account'          => $this->generateRandomAccount().$index,
                'password'         => str_random(),
                'customer_id'      => $customer_id,
                'device_id'        => $device->id ?? 0,
//                'skin_detector_id' => $device->skinDetector->id ?? 0, // 改成多对多了
                'device_name'      => $device ? $device->device_name . '-' . $device->specification : null,
                'uuid'             => $uuids[$index],
                'sell_device_id'   => $sell_device->id,
                'created_at'       => Carbon::now(),
                'updated_at'       => Carbon::now(),
            ];
        }

        (new DeviceAccount)->insert($new_accounts);
    }

    /**
     * @param SellDevice $sell_device
     * @param array|null $skin_detector_ids
     * @return bool
     */
    private function storeDeviceAccountPivotSkinDetector(SellDevice $sell_device, ?array $skin_detector_ids)
    {
        if ( is_null( $skin_detector_ids ) ) return false;
        $device_account_ids = $sell_device->deviceAccounts()->pluck('id')->all();

        $data = [];
        foreach($device_account_ids as $device_account_id)
        {
            foreach($skin_detector_ids as $skin_detector_id)
            {
                $data[] = ['device_account_id'=>$device_account_id, 'skin_detector_id'=>$skin_detector_id];
            }
        }

       return (new DeviceAccountPivotSkinDetector)->insert($data);
    }

    /**
     * @return bool|string
     */
    private function generateRandomAccount(): string
    {
        $account = md5(microtime());

        return $account;
    }

    /**
     * 将 UUIDs 补位成对 num 对应的 uuid 数组，
     * @example
     *      if $req->num is 5
     *      then  null            => ['uuid-1', 'uuid-2', 'uuid-3', 'uuid-4', 'uuid-5']
     *      or   [null, 'uuid2']  => ['uuid-6', 'uuid2', 'uuid-7', 'uuid-8', 'uuid-9']
     *
     *
     * @param DeviceSellRequest $request
     *
     * @return array
     */
    protected function normalizeUUIDs(DeviceSellRequest $request): array
    {
        $pad_arr = array_pad((array) $request->uuids, $request->num, null);

        return array_map(function ($uuid) {
            return $uuid ?: Uuid::generate();
        }, $pad_arr);
    }

    /**
     * ? 这里为什么要加个 skin_detectors
     *
     * @param LengthAwarePaginator $sell_devices
     */
    private function transformData(LengthAwarePaginator $sell_devices)
    {
        $sell_devices->transform(function(SellDevice $sell_device){
            $sell_device->skin_detectors = $sell_device->deviceAccounts->first()->skinDetectors ?? [];

            return $sell_device;
        });
    }

    private function assembleDeviceAccountData(DeviceUpdateDeviceAccountRequest $request)
    {
        $data = [
            'account' => $request->account,
        ];

        if ($request->password) {
            $data['password'] = $request->password;
            $data['password_modified_at'] = now();
        }

        return $data;
    }
}
